﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Web;

namespace Mozlite
{
    /// <summary>
    /// 类型扩张。
    /// </summary>
    public static class TypeExtensions
    {
        /// <summary>
        /// 将字符串类型转换为<typeparamref name="T"/>类型。
        /// </summary>
        /// <typeparam name="T">转换后的类型。</typeparam>
        /// <param name="value">用于转换的对象。</param>
        /// <param name="defaultValue">默认值，即转换失败后返回的值。</param>
        /// <returns>返回转换后的实例对象。</returns>
        public static T To<T>(this object value, T defaultValue = default(T))
        {
            return (T)value.ChangeType(typeof(T), defaultValue);
        }

        /// <summary>
        /// 实体类型赋值。
        /// </summary>
        /// <typeparam name="T">实体类型。</typeparam>
        /// <param name="nvc">请求实例。</param>
        /// <returns>返回实例对象。</returns>
        public static T As<T>(this NameValueCollection nvc) where T : class, new()
        {
            T value = new T();
            Type type = typeof(T);
            foreach (PropertyInfo p in type.GetProperties(BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.SetProperty))
            {
                try
                {
                    p.SetValue(value, nvc[p.Name].ChangeType(p.PropertyType), null);
                }
                catch
                {
                }
            }
            return value;
        }

        /// <summary>
        /// 实体类型赋值。
        /// </summary>
        /// <typeparam name="T">实体类型。</typeparam>
        /// <param name="nvc">请求实例。</param>
        /// <param name="name">名称。</param>
        /// <param name="defaultValue">默认值。</param>
        /// <returns>返回实例对象。</returns>
        public static T Get<T>(this NameValueCollection nvc, string name, T defaultValue = default(T))
        {
            return nvc[name].To<T>(defaultValue);
        }

        /// <summary>
        /// 实体类型赋值。
        /// </summary>
        /// <typeparam name="T">实体类型。</typeparam>
        /// <param name="request">请求实例。</param>
        /// <returns>返回实例对象。</returns>
        public static T As<T>(this HttpRequest request) where T : class, new()
        {
            T value = new T();
            Type type = typeof(T);
            foreach (PropertyInfo p in type.GetProperties(BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.SetProperty))
            {
                try
                {
                    p.SetValue(value, request[p.Name].ChangeType(p.PropertyType), null);
                }
                catch
                {
                }
            }
            return value;
        }

        /// <summary>
        /// 实体类型赋值。
        /// </summary>
        /// <typeparam name="T">实体类型。</typeparam>
        /// <param name="request">请求实例。</param>
        /// <param name="name">名称。</param>
        /// <param name="defaultValue">默认值。</param>
        /// <returns>返回实例对象。</returns>
        public static T Get<T>(this HttpRequest request, string name, T defaultValue = default(T))
        {
            return request[name].To<T>(defaultValue);
        }

        /// <summary>
        /// 将字符串类型转换为<typeparamref name="T"/>类型。
        /// </summary>
        /// <typeparam name="T">转换后的类型。</typeparam>
        /// <param name="strValue">用于转换的对象。</param>
        /// <param name="type">转换的类型。</param>
        /// <param name="defaultValue">默认值，即转换失败后返回的值。</param>
        /// <returns>返回转换后的实例对象。</returns>
        public static object ChangeType(this object value, Type type, object defaultValue = null)
        {
            if (value == null)
                return defaultValue;
            Type nullableType = Nullable.GetUnderlyingType(type);
            if (nullableType != null)
                type = nullableType;
            try
            {
                switch (Type.GetTypeCode(type))
                {
                    case TypeCode.Boolean:
                        return Convert.ToBoolean(value);
                    case TypeCode.Byte:
                        return Convert.ToByte(value);
                    case TypeCode.Char:
                        return Convert.ToChar(value);
                    case TypeCode.DateTime:
                        return Convert.ToDateTime(value);
                    case TypeCode.Decimal:
                        return Convert.ToDecimal(value);
                    case TypeCode.Double:
                        return Convert.ToDouble(value);
                    case TypeCode.Int16:
                        return Convert.ToInt16(value);
                    case TypeCode.Int32:
                        return Convert.ToInt32(value);
                    case TypeCode.Int64:
                        return Convert.ToInt64(value);
                    case TypeCode.SByte:
                        return Convert.ToSByte(value);
                    case TypeCode.Single:
                        return Convert.ToSingle(value);
                    case TypeCode.String:
                        return value.ToString();
                    case TypeCode.UInt16:
                        return Convert.ToUInt16(value);
                    case TypeCode.UInt32:
                        return Convert.ToUInt32(value);
                    case TypeCode.UInt64:
                        return Convert.ToUInt64(value);
                    case TypeCode.Empty:
                    case TypeCode.DBNull:
                        return defaultValue;
                    default:
                        if (type == typeof(Guid))
                        {
                            try
                            {
                                return (Guid)value;
                            }
                            catch {
                                try { return new Guid(value.ToString()); }
                                catch {
                                    return defaultValue == null ? Guid.Empty : (Guid)defaultValue;
                                }
                            }
                        }
                        else if (type == typeof(Encoding))
                        {
                            try
                            {
                                return Encoding.GetEncoding(value.ToString());
                            }
                            catch {
                                return defaultValue;
                            }
                        }
                        else if (type == typeof(Version))
                        {
                            try
                            {
                                return new Version(value.ToString());
                            }
                            catch
                            {
                                return defaultValue;
                            }
                        }
                        break;
                }
            }
            catch
            {
                if (type.IsEnum)
                {
                    try
                    {
                        return Enum.Parse(type, value.ToString(), true);
                    }
                    catch { }
                }
            }
            return defaultValue;
        }

        /// <summary>
        /// 获取格式化后的字符串。
        /// </summary>
        /// <param name="length">文档长度。</param>
        /// <param name="digit">小数位数。</param>
        /// <returns>返回格式化后的字符串。</returns>
        public static string ToSizeString(this long length, int digit = 2)
        {
            if(length >= Consts.TBytes)
                return string.Format("{0:f" + digit + "} TB", length * 1.0 / Consts.TBytes);
            if (length >= Consts.GBytes)
                return string.Format("{0:f" + digit + "} GB", length * 1.0 / Consts.GBytes);
            if (length >= Consts.MBytes)
                return string.Format("{0:f" + digit + "} MB", length * 1.0 / Consts.MBytes);
            if (length >= Consts.KBytes)
                return string.Format("{0:f" + digit + "} KB", length * 1.0 / Consts.KBytes);
            return length + " B";
        }

        /// <summary>
        /// 获取格式化后的字符串。
        /// </summary>
        /// <param name="length">文档长度。</param>
        /// <param name="digit">小数位数。</param>
        /// <returns>返回格式化后的字符串。</returns>
        public static string ToSizeString(this int length, int digit = 2)
        {
            return ((long)length).ToSizeString(digit);
        }

        /// <summary>
        /// 获取格式化后的字符串。
        /// </summary>
        /// <param name="length">文档长度。</param>
        /// <param name="digit">小数位数。</param>
        /// <returns>返回格式化后的字符串。</returns>
        public static string ToSizeString(this uint length, int digit = 2)
        {
            return ((long)length).ToSizeString(digit);
        }

        /// <summary>
        /// 获取16进制的字符串。
        /// </summary>
        /// <param name="buffer">加密后的字节。</param>
        /// <returns>返回16进制的字符串。</returns>
        public static string ToHex(this byte[] buffer) {
            return BitConverter.ToString(buffer).Replace("-", "");
        }

        /// <summary>
        /// 获取Base64的字符串。
        /// </summary>
        /// <param name="buffer">加密后的字节。</param>
        /// <returns>返回Base64的字符串。</returns>
        public static string ToBase64(this byte[] buffer)
        {
            return Convert.ToBase64String(buffer);
        }

        /// <summary>
        /// 返回包含命名空间在内的字符串。
        /// </summary>
        /// <param name="type">类型实例。</param>
        /// <returns>返回包含命名空间在内的字符串。</returns>
        public static string GetFullName(this Type type) {
            return string.Format("{0}, {1}", type.FullName, type.Namespace);
        }
        /// <summary>
        /// 判断是否为忽略属性。
        /// </summary>
        /// <param name="info">属性实例。</param>
        /// <returns>返回判断结果。</returns>
        public static bool IsIgnore(this PropertyInfo info)
        {
            var attrs = info.GetCustomAttributes(typeof(PropertyIgnoreAttribute), true);
            return attrs.Length > 0;
        }
    }
}
